package com.goldsprite.gameframeworks.input;

import com.badlogic.gdx.InputAdapter;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;

public class InputSystem extends InputAdapter {
	private static InputSystem instance;
	private final Map<Consumer<?>, Consumer<InputActionContext>> proxys = new HashMap<>();
	private static boolean enablePrintLog = false;
	public static Consumer<InputActionContext> printLogConsumer = context -> {
		printLog(context.triggeringBinding.action);
	};
	private InputActions inputActions;
	public static String lastPrintLog;

	public static InputSystem getInstance() {
		if (instance == null) {
			instance = new InputSystem();
		}
		return instance;
	}

	public static void registerPrintLogListener(InputAction action) {
		action.started.add(printLogConsumer);
		action.performed.add(printLogConsumer);
		action.canceled.add(printLogConsumer);
	}

	public InputActions getInputActions() {
		return inputActions;
	}

	public void setInputActions(InputActions inputActions) {
		this.inputActions = inputActions;
	}

	public static <T> void registerActionListener(InputAction action, InputActionState registerType, Consumer<T> listener) {
		registerActionListener(action, registerType, listener, false);
	}
	public static <T> void registerActionListener(InputAction action, InputActionState registerType, Consumer<T> listener, boolean printLog) {

		Consumer<InputActionContext> proxy =
			c -> {
				T val = (T) action.readValue(action.getValueType());
				listener.accept(val);
		};
		if (InputActionState.Started.equals(registerType)) action.started.add(proxy);
		if (InputActionState.Performed.equals(registerType)) action.performed.add(proxy);
		if (InputActionState.Canceled.equals(registerType)) action.canceled.add(proxy);

//		proxys.put(listener, proxy);
	}

	@Override
	public boolean keyDown(int keyboardCode) {
		//遍历按键行为表的每个action中的每个binding
		for (InputAction action : inputActions.keyActions.values()) {
			for (InputBinding binding : action.inputBindings) {
				//未匹配到按键码跳过
				if (!binding.matchesKey(keyboardCode)) continue;
				//未开始则调用开始回调
				if (!action.isStarted()) {
					binding.updateValue();//更新值
					//更新上下文
					action.inputActionContext.triggeringBinding = binding;
					action.inputActionContext.state = InputActionState.Started;
					action.started.invoke(action.inputActionContext);//调用回调
					action.setStarted(true);
				}else if(binding.updateValue()){
					//更新上下文
					action.inputActionContext.triggeringBinding = binding;
					action.inputActionContext.state = InputActionState.Performed;
					//有更新且有输入则调用持续回调
					action.performed.invoke(action.inputActionContext);
				}
				//找到一组binding后打断，因为不同组映射无法一起检测：A和→就不行
				break;
			}
		}
		return super.keyDown(keyboardCode);
	}

	@Override
	public boolean keyUp(int keyboardCode) {
		//遍历按键行为表的每个action中的每个binding
		for (InputAction action : inputActions.keyActions.values()) {
			boolean isMatches = false, isActive = false;
			for (InputBinding binding : action.inputBindings) {
				//未匹配到按键码跳过
				if (!binding.matchesKey(keyboardCode)) continue;
				else isMatches = true;
				//匹配到则更新值
				if (binding.updateValue() && binding.isHold()) {
					//更新上下文
					action.inputActionContext.triggeringBinding = binding;
					action.inputActionContext.state = InputActionState.Performed;
					//有更新且有输入则调用持续回调
					action.performed.invoke(action.inputActionContext);
				}
				//当仍有任意绑定有输入时记录为激活
				if (binding.isHold()) isActive = true;
			}
			//所有绑定都无输入则调用释放回调
			if (isMatches && !isActive) {
				//更新上下文
				action.inputActionContext.state = InputActionState.Canceled;
				action.canceled.invoke(action.inputActionContext);
				action.setStarted(false);
			}
		}
		return super.keyUp(keyboardCode);
	}

	public void update(float delta) {
//		if(true) return;
		//遍历按键行为表的每个action中的每个binding
		for (InputAction action : inputActions.keyActions.values()) {
			for (InputBinding binding : action.inputBindings) {
				boolean isModified = binding.updateValue();
				boolean isHold = binding.isHold();
				//虚拟触控输入
				if (binding instanceof IVirtualBinding) {
					//未开始且有更新 开始
					if (!action.isStarted() && isModified) {
						action.setStarted(binding, true);
						//更新上下文
						action.inputActionContext.triggeringBinding = binding;
						action.inputActionContext.state = InputActionState.Started;
						action.started.invoke(action.inputActionContext);
						break;
					}
					//已开始且无输入 取消
					if (action.isStarted(binding) && !isHold) {
						action.setStarted(binding, false);
						//更新上下文
						action.inputActionContext.triggeringBinding = binding;
						action.inputActionContext.state = InputActionState.Canceled;
						action.canceled.invoke(action.inputActionContext);
						break;
					}
				}

				//已开始且有值变更(且有值时) 持续回调
				if (action.isStarted() && isHold) {
					//更新上下文
					action.inputActionContext.triggeringBinding = binding;
					action.inputActionContext.state = InputActionState.Performed;
					action.performed.invoke(action.inputActionContext);
					//找到一组binding后打断， 因为不同组映射无法一起检测：A和→就不行
					break;
				}
			}
		}
	}

	private static void printLog(InputAction action) {
		lastPrintLog = "" +
			"InputAction: " + action.name +
			", Binding: " + action.inputActionContext.triggeringBinding.name +
			", InputActionState: " + action.inputActionContext.state +
			", val: " + action.readValueAsObject()
		;
		if(!enablePrintLog) return;

		System.out.println(lastPrintLog);
	}

	@Override
	public boolean touchDown(int screenX, int screenY, int pointer, int button) {
		return super.touchDown(screenX, screenY, pointer, button);
	}

	@Override
	public boolean touchUp(int screenX, int screenY, int pointer, int button) {
		return super.touchUp(screenX, screenY, pointer, button);
	}
}
